package com.loong.tool.secret.security.encrypt;

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

import com.loong.tool.secret.base.Base64;
import com.loong.tool.secret.base.UTF8;
import com.loong.tool.secret.exception.InvalidCodeException;
import com.loong.tool.secret.exception.InvalidCryptoException;
import com.loong.tool.secret.exception.InvalidKeyException;
import com.loong.tool.secret.exception.SecretVariableException;

/**
 * 非对称加密处理类
 * 
 * @author 张成轩
 */
public class Asymmetric {

	/**
	 * 加密算法
	 */
	public enum Algorithm {

		/** RSA算法 */
		RSA("RSA");

		private String value;

		private Algorithm(String value) {

			this.value = value;
		}
	}

	/** 默认加密类型 */
	private static final Algorithm DEFAULT = Algorithm.RSA;

	/**
	 * 生成随机密钥
	 * 
	 * @return 密钥
	 */
	public static AsymmKey getKey() {

		// 获取密钥对象
		return getKey(DEFAULT);
	}

	/**
	 * 生成随机密钥
	 * 
	 * @param algorithm 算法
	 * @return 密钥
	 */
	public static AsymmKey getKey(Algorithm algorithm) {

		/* 实例化密钥生成对象 */
		KeyPairGenerator kpg;
		try {
			kpg = KeyPairGenerator.getInstance(algorithm.value);
		} catch (NoSuchAlgorithmException e) {
			throw new SecretVariableException("Asymmetric variable init error: " + algorithm.name());
		}
		/* 生成密钥对 */
		KeyPair kp = kpg.generateKeyPair();

		/* 获取公钥字节数组 */
		String publicKey = Base64.encode(kp.getPublic().getEncoded());
		/* 获取私钥字节数组 */
		String privateKey = Base64.encode(kp.getPrivate().getEncoded());

		return new AsymmKey(publicKey, privateKey);
	}

	/**
	 * 获取公钥对象
	 * 
	 * @param publicKey 公钥
	 * @param algorithm 算法
	 * @return 公钥对象
	 * @throws InvalidKeyException 无效密钥异常
	 */
	private static PublicKey getPublicKeySpec(String publicKey, Algorithm algorithm) throws InvalidKeyException {

		/* 实例化公钥处理对象 */
		X509EncodedKeySpec x509Encoded = new X509EncodedKeySpec(Base64.decode(publicKey));

		PublicKey pk;
		try {
			/* 实例化密钥处理工厂对象 */
			KeyFactory keyFactory = KeyFactory.getInstance(algorithm.value);
			/* 生成公钥 */
			pk = keyFactory.generatePublic(x509Encoded);

		} catch (NoSuchAlgorithmException e) {
			throw new SecretVariableException("Asymmetric variable init error: " + algorithm.name());
		} catch (InvalidKeySpecException | InvalidCodeException e) {
			throw new InvalidKeyException(e);
		}
		return pk;
	}

	/**
	 * 获取私钥对象
	 * 
	 * @param privateKey 私钥
	 * @param algorithm 算法
	 * @return 私钥对象
	 * @throws InvalidKeyException 无效密钥异常
	 */
	private static PrivateKey getPrivateKeySpec(String privateKey, Algorithm algorithm) throws InvalidKeyException {

		/* 实例化私钥处理对象 */
		PKCS8EncodedKeySpec pkcs8Encoded = new PKCS8EncodedKeySpec(Base64.decode(privateKey));

		PrivateKey pk;
		try {
			/* 实例化密钥处理工厂对象 */
			KeyFactory keyFactory = KeyFactory.getInstance(algorithm.value);
			/* 生成私钥 */
			pk = keyFactory.generatePrivate(pkcs8Encoded);

		} catch (NoSuchAlgorithmException e) {
			throw new SecretVariableException("Asymmetric variable init error: " + algorithm.name());
		} catch (InvalidKeySpecException | InvalidCodeException e) {
			throw new InvalidKeyException(e);
		}
		return pk;
	}

	/**
	 * 公钥加密
	 * 
	 * @param str 明文
	 * @param publicKey 公钥
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String publicEncrypt(String str, String publicKey) throws InvalidKeyException {

		return publicEncrypt(str, publicKey, DEFAULT);
	}

	/**
	 * 公钥加密
	 * 
	 * @param str 明文
	 * @param publicKey 公钥
	 * @param algorithm 算法
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String publicEncrypt(String str, String publicKey, Algorithm algorithm) throws InvalidKeyException {

		return encrypt(str, getPublicKeySpec(publicKey, algorithm), algorithm);
	}

	/**
	 * 私钥加密
	 * 
	 * @param str 明文
	 * @param privateKey 私钥
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String privateEncrypt(String str, String privateKey) throws InvalidKeyException {

		return privateEncrypt(str, privateKey, DEFAULT);
	}

	/**
	 * 私钥加密
	 * 
	 * @param str 明文
	 * @param privateKey 私钥
	 * @param algorithm 算法
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String privateEncrypt(String str, String privateKey, Algorithm algorithm) throws InvalidKeyException {

		return encrypt(str, getPrivateKeySpec(privateKey, algorithm), algorithm);
	}

	/**
	 * 加密
	 * 
	 * @param str 明文
	 * @param key 密钥对象
	 * @param algorithm 加密算法
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	private static String encrypt(String str, Key key, Algorithm algorithm) throws InvalidKeyException {

		try {
			// 密钥处理对象
			Cipher cipher = Cipher.getInstance(algorithm.value);
			// 设置加密模式
			cipher.init(Cipher.ENCRYPT_MODE, key);
			return Base64.encode(cipher.doFinal(UTF8.getBytes(str)));

		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new SecretVariableException("Asymmetric variable init error: " + algorithm.name());
		} catch (GeneralSecurityException e) {
			throw new InvalidKeyException(e);
		}
	}

	/**
	 * 公钥解密
	 * 
	 * @param crypto 密文
	 * @param publicKey 公钥
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String publicDecrypt(String crypto, String publicKey)
			throws InvalidKeyException, InvalidCryptoException {

		return publicDecrypt(crypto, publicKey, DEFAULT);
	}

	/**
	 * 公钥解密
	 * 
	 * @param crypto 密文
	 * @param publicKey 公钥
	 * @param algorithm 算法
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String publicDecrypt(String crypto, String publicKey, Algorithm algorithm)
			throws InvalidKeyException, InvalidCryptoException {

		return decrypt(crypto, getPublicKeySpec(publicKey, algorithm), algorithm);
	}

	/**
	 * 私钥解密
	 * 
	 * @param crypto 密文
	 * @param privateKey 私钥
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String privateDecrypt(String crypto, String privateKey)
			throws InvalidKeyException, InvalidCryptoException {

		return privateDecrypt(crypto, privateKey, DEFAULT);
	}

	/**
	 * 私钥解密
	 * 
	 * @param crypto 密文
	 * @param privateKey 私钥
	 * @param algorithm 算法
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String privateDecrypt(String crypto, String privateKey, Algorithm algorithm)
			throws InvalidKeyException, InvalidCryptoException {

		return decrypt(crypto, getPrivateKeySpec(privateKey, algorithm), algorithm);
	}

	/**
	 * 解密
	 * 
	 * @param crypto 密文
	 * @param key 密钥对象
	 * @param algorithm 算法
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	private static String decrypt(String crypto, Key key, Algorithm algorithm)
			throws InvalidKeyException, InvalidCryptoException {

		try {

			// 密钥处理对象
			Cipher cipher = Cipher.getInstance(algorithm.value);
			// 设置解密模式
			cipher.init(Cipher.DECRYPT_MODE, key);
			return UTF8.newString(cipher.doFinal(Base64.decode(crypto)));

		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new SecretVariableException("Asymmetric variable init error: " + algorithm.name());
		} catch (KeyException e) {
			throw new InvalidKeyException(e);
		} catch (GeneralSecurityException | InvalidCodeException e) {
			throw new InvalidCryptoException(e);
		}
	}

	/**
	 * 非对称密钥
	 * 
	 * @author 张成轩
	 */
	public static class AsymmKey {

		/** 公钥 */
		private String publicKey;
		/** 私钥 */
		private String privateKey;

		private AsymmKey(String publicKey, String privateKey) {

			this.publicKey = publicKey;
			this.privateKey = privateKey;
		}

		/**
		 * @return 公钥
		 */
		public String getPublicKey() {

			return publicKey;
		}

		/**
		 * @return 私钥
		 */
		public String getPrivateKey() {

			return privateKey;
		}
	}
}
